package com.example.demo.services.system;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.IncorrectClaimException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.demo.models.admin.Admin;
import com.example.demo.models.base.JWTSubject;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JWTService {

    private static String KEY = "TEST";
    private static final String ISS = "http://localhost/";

    //用于进行名字转换
    private static final Map<String, String> name2jwt = new HashMap<>();

    static {
        name2jwt.put("id", "sub");
        name2jwt.put("class", "aud");
    }

    //可参考这个类新建JWT
    static class JWTToken {
        //颁发者
        public String iss;
        //JWT生成时间
        public long iat;
        //JWT过期时间（已经自带）
        public long exp;
        //JWT生效时间（小于这个时间不生效）
        public long nbf;
        //JWT的唯一id
        public String jti;
        //JWT的主角，就是颁发的时候是谁的，可以填用户id
        public String sub;
        //JWT的受众，可以填class的hashcode
        public String aud;

    }

    public static void main(String[] args) throws InterruptedException {
        //别用这个生成，classId是不匹配的
        Admin admin = new Admin();
        admin.setId(1L);
        String token = generateToken(admin);
        System.out.println(token);
//        Thread.sleep(5000);
//        DecodedJWT jwt = verify(token);
//        System.out.println(jwt.getClaim("jti").asString());
//        System.out.println(jwt.getClaim("sub").asString());
    }

    /**
     * 修改过期时间（3小时）
     *
     * @return
     */
    private static Calendar getCalendar() {
        // 默认3小时过期
        return getCalendar(Calendar.HOUR, 3);
    }

    private static Calendar getCalendar(int field, int amount) {
        Calendar instance = Calendar.getInstance();
        // 默认1小时过期
        instance.add(field, amount);
        return instance;
    }

    /**
     * 基于用户生成一个JWT token
     *
     * @param user
     * @return
     */
    public static String generateToken(JWTSubject user) {
        //创建jwt builder
        JWTCreator.Builder builder = JWT.create();

        //放置基础信息
        //颁发者
        builder.withIssuer(ISS);
        //JWT生成时间
        Date iat = Calendar.getInstance().getTime();
        builder.withIssuedAt(iat);
        //JWT过期时间自带
        //JWT最早生效时间
        builder.withNotBefore(iat);

        //JWT记录的用户
        //用户类哈希
        String classId = String.valueOf(user.getJWTClassId());
        //用户id
        String userId = user.getJWTIdentifier();
        //JWT唯一标识
        builder.withJWTId(userId + "X" + classId + "x" + iat.getTime());
        //JWT的主角（就是用户id）
        builder.withSubject(userId);
        //JWT的受众（用来作为角色类标识）
        //builder.withAudience(classId);
        builder.withClaim("aud", classId);

        //如果需要额外加入信息
        if (user.getJWTCustomClaims() != null) {
            user.getJWTCustomClaims().forEach(builder::withClaim);
        }
        //生成token
        String token = builder.withExpiresAt(getCalendar().getTime())
                .sign(Algorithm.HMAC256(KEY));
        return token;
    }

    /**
     * 验证并返回token数据
     * 如果token出错会抛出错误
     *
     * @param token
     * @return
     */
    public static DecodedJWT verify(String token) throws JWTVerificationException {
        DecodedJWT jwt = null;
        try {
            //先验证看看签名、时间能不能过
            jwt = JWT.require(Algorithm.HMAC256(KEY)).build().verify(token);
        } catch (SignatureVerificationException e) {
            //e.printStackTrace();
            throw new JWTVerificationException("The token is not valid the signature");
        } catch (TokenExpiredException e) {
            //e.printStackTrace();
            throw new JWTVerificationException("The token has expired");
        } catch (IncorrectClaimException e) {
            throw new JWTVerificationException("The token can't be used now");
        }
//        //现在
//        Calendar now = Calendar.getInstance();
//        //然后判断时间是否早于
//        Calendar iat = Calendar.getInstance();
//        iat.setTimeInMillis(jwt.getClaim("nbf").asLong());
//        if (now.before(iat)) {
//            //时间太早，所以失效了
//            throw new JWTVerificationException("The token is not yet valid (nbf)");
//        }
        return jwt;
    }


    public static String name2jwt(String key) {
        return name2jwt.get(key);
    }
}
